<?php
/**
* @package phpBB3 ATK - Admin Tool Kit
* @version $Id: atk_core.php 195 2009-01-16 15:41:29Z ej.frerejean $
* @copyright (c) 2008 Admin Tool Kit community development team
* @author Erik Frèrerejean (Erik Frèrejean)
* @author Nathon Guse (EXreaction)
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/

/**
* @ignore
*/
if (!defined('IN_ATK'))
{
	exit;
}

class atk_core
{
	/**
	* ATK config
	*
	* @access private
	*/
	var $config = array();
	
	/**
	* Data that defines the requested tool.
	* On load this array holds the default values
	*
	* @access private
	*/
	var $req_tool_data = array(
		'submitted'		=> false,
		'req_action'	=> 'none',
		'req_tool'		=> '',
	);
	
	/**
	* The ATK phpBB class
	*/
	var $phpbb = NULL;
	
	/**
	* The tool that is active
	*/
	var $tool = NULL;
	

	/**
	* Main constructor
	* Setup the class and load the config array
	*
	* @param array &$config The content of atk_config, we will set this to NULL after loading to prevent issues with the phpBB version
	* @access public
	*/
	function __construct()
	{
		// Load the config
		if (false === (@include (ATK_ROOT_PATH . 'atk_config.' . PHP_EXT)))
		{
			trigger_error(ATK_ROOT_PATH . 'atk_config.' . PHP_EXT . ' couldn\'t be found. Please check your ATK installation!', E_USER_ERROR);
		}

		// Now load the libraries from the includes/lib folder. All files prefixed with an underscore are ONLY included in stand alone mode
		if ($handle = opendir(ATK_LIB_PATH))
		{
			while (false !== ($lib = readdir($handle)))
			{
				// Skip "_" prefixed if not stand alone, and skip *nix hidden files
				if ((!$this->config['atk_stand_alone'] && (substr($lib, 0, 1) == '_')) || substr($lib, 0, 1) == '.')
				{
					continue;
				}
		
				// Skip directories
				//if (is_dir($lib))	=> For some reason doesn't this work :?
				if (strpos($lib, PHP_EXT) === false)
				{
					continue;
				}

				// Load the file
				require ATK_LIB_PATH . $lib;
			}
			
			closedir($handle);
		}
		
		// Load the phpBB class
		$this->phpbb = new atk_phpbb();
		
		// Run the un authed tools
		$this->_run_non_authed_tools();
	}

	//-- Login methods
	
	/**
	* login
	* Determain which login method we need to use and call the correct one
	*
	* @access public
	*/
	function login()
	{
		global $user;

		switch (true)
		{
			case (!$user->data['is_registered'] && $this->get_config('atk_password')) :
				$this->_atk_login();
			break;
			
			default :
				$this->_login();
		}
	}
	
	/**
	* Our custom login method.
	* This requires a cookie to stay logged in.
	*
	* @access private
	*/
	function _atk_login()
	{
		global $config, $user;
		
		// Get the cookie, this is mode dependant!
		$cookie_name	= ((ATK_PANIC) ? 'phpbb_atk_key' : $config['cookie_name'] . '_atk_key');
		$atk_key		= request_var($cookie_name, '', true, true);
		
		$loged_in	= false;
		
		// Do we have a key?
		if (!empty($atk_key) && strlen($atk_key) == 34)
		{
			// Check the stored hash
			if (phpbb_check_hash($this->get_config('atk_password'), $atk_key))
			{
				$loged_in = true;
			}
		}
		
		if (!$loged_in && isset($_POST['login']))
		{
			$credential = request_var('credential', '');

			if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
			{
				// @TODO:
				trigger_error('Invalid form credential!', E_USER_ERROR);
			}
			
			$password = request_var('password_' . $credential, '', true);

			// Check
			if (phpbb_check_hash($password, phpbb_hash($this->get_config('atk_password'))))
			{
				// Set the cookie which is valid for as long this session is valid
				$user->set_cookie('atk_key', phpbb_hash($password), 0);
				
				$loged_in = true;
			}
		}
		
		if (!$loged_in)
		{
			// Generate login page
			global $template;
			
			$credential = md5(unique_id());
			$s_hidden_fields = array('credential' => $credential);
			
			$template->assign_vars(array(
				'PASSWORD_CREDENTIAL'	=> 'password_' . $credential,
				'S_HIDDEN_FIELDS'		=> build_hidden_fields($s_hidden_fields),
				'S_LOGIN_ACTION'		=> append_sid(ATK_ROOT_PATH . 'index.' . PHP_EXT),
			));

			$this->phpbb->page_header('atk_login_body', 'LOGIN');
			$this->phpbb->page_footer();
		}
	}
	
	/**
	* The phpBB login
	*
	* @access private
	*/
	function _login()
	{
		global $user;
		
		if (!$user->data['is_registered'])
		{
			// Could get here if no ATK password is set.
			login_box(ATK_ROOT_PATH);
		}

		// This requires that the user is logged in as an administrator (like how the ACP requires two logins)
		if (!isset($user->data['session_admin']) || !$user->data['session_admin'])
		{
			login_box(ATK_ROOT_PATH, $user->lang['LOGIN_ADMIN_CONFIRM'], $user->lang['LOGIN_ADMIN_SUCCESS'], true, false);
		}

		// Only Board Founders may use the ATK
		if ($user->data['user_type'] != USER_FOUNDER)
		{
			trigger_error('BOARD_FOUNDER_ONLY');
		}
	}


	//-- Tool methods
	/**
	* Wrapper to call the correct tool method
	*
	* @access public
	*/
	function tool_box()
	{
		// Set the $req_tool_data array
		$this->req_tool_data = array(
			'submitted'		=> (isset($_POST['submit'])) ? true : false,
			'req_action'	=> request_var('action', $this->req_tool_data['req_action']),
			'req_tool'		=> request_var('t', $this->req_tool_data['req_tool']),
		);
		
		
		// Call the correct method
		switch (true)
		{
			case !$this->req_tool_data['submitted'] && empty($this->req_tool_data['req_tool']) :
				$this->_list_tools();
			break;

			case !$this->req_tool_data['submitted'] && !empty($this->req_tool_data['req_tool']) :
				$this->_tool_options();
			break;
			
			case $this->req_tool_data['submitted'] && !empty($this->req_tool_data['req_tool']) :
				$this->_run_tool();

				$errors = $this->tool->get_tool_errors();
				if (!sizeof($errors))
				{
					trigger_error('TOOL_FINISHED');
				}
				else
				{
					trigger_error(implode('<br />', $errors));
				}
			break;
			
			default :
				// Shouldn't happen, thus exit
				exit;
		}
	}
	
	/**
	* Create a options page based upon the given options array
	*/
	function _display_options($options)
	{
		global $template;
		
		if (!is_array($options))
		{
			return;
		}
		
		// Loop through them and build the template
		foreach ($options as $name => $options)
		{
			if (!is_array($options) && strpos($name, 'legend') === false)
			{
				continue;
			}
			
			if (strpos($name, 'legend') !== false)
			{
				update_lang_string($options);
				
				$template->assign_block_vars('tooloptions', array(
					'S_LEGEND'	=> true,
					'LEGEND'	=> $options,
				));
				
				continue;
			}

			$type = explode(':', $options['type']);
			
			$content = build_cfg_template($type, $name, $options);
			
			if (!sizeof($content))
			{
				continue;
			}

			$l_explain = '';
			if ($options['explain'] && isset($options['lang_explain']))
			{
				$l_explain = update_lang_string($options['lang_explain'], true);
			}
			else if ($options['explain'])
			{
				$l_explain = (get_lang_string($options['lang'] . '_EXPLAIN')) ? get_lang_string($options['lang'] . '_EXPLAIN') : '';
			}
			
			update_lang_string($options['lang']);
			
			// Assign
			$template->assign_block_vars('tooloptions', array(
				'CONTENT'		=> $content['tpl'],
				'KEY'			=> $name,
				'S_EXPLAIN'		=> $options['explain'],
				'TITLE'			=> $options['lang'],
				'TITLE_EXPLAIN'	=> $l_explain,
			));
			
			// If we have a find user link add it to the block
			// Use alter_block_array to prevent any dirty work arounds inside the assign_block_vars
			if (isset($content['find_user']))
			{
				$template->alter_block_array('tooloptions', array(
					'S_FIND_USER'	=> true,
					'U_FIND_USER'	=> append_sid(PHPBB_ROOT_PATH . 'memberlist.' . PHP_EXT, array('mode' => 'searchuser', 'from' => 'select_user', 'field' => 'username', 'select_single' => 'true', 'form' => 'admin_tool_kit', 'field' => $content['find_user_field'])),
				), true);
			}
		}
	}
	
	/**
	* Display the available tools.
	* This overview is created by looking in the ATK_TOOL_BOX directory. All files prefixed with an
	* "_" (underscore) will only be displayed when in stand alone mode.
	*
	* @access private
	*/
	function _list_tools()
	{
		global $template, $user;
		
		// Use the caching only if not stand alone.
		$tools = false;
		if (!ATK_PANIC)
		{
			global $cache;
			$tools = $cache->get('_atk_tools');
		}

		// Have to build the list?
		if ($tools === false)
		{
			$tools = array();
			
			// Loop through the tool box
			if (false !== ($handle = opendir(ATK_TOOL_BOX)))
			{
				while (false !== ($file = readdir($handle)))
				{
					// Skip . , .. and *NIX hidden files
					if (substr($file, 0, 1) == '.')
					{
						continue;
					}

					// Only fetch the correct tool (based upon stand alone yes/no)
					if (ATK_PANIC && substr($file, 0, 1) != '_')
					{
						continue;
					}
					else if (!ATK_PANIC && substr($file, 0, 1) == '_')
					{
						continue;
					}
					
					// Fetch and load this tool
					$tool = $this->_load_tool($file);

					// Skip dev tools when not in debug mode
					if (!$this->get_config('act_dev_tools') && $tool->get_category() == 'DEV')
					{
						unset ($tool);
						continue;
					}
					
					// Add the instance
					if (!isset($tools[$tool->get_category()]))
					{
						$tools[$tool->get_category()] = array();
					}
					$tools[$tool->get_category()][] = $tool->get_info();
				}
			}
			
			closedir($handle);
		}

		// When not running stand alone cache this list
		if (!ATK_PANIC)
		{
			// @TODO: Remove comment
			//$cache->put('_atk_tools', $tools);
		}
		
		// Assign the data
		foreach ($tools as $tool_cat => $cat_tools)
		{
			$template->assign_block_vars('toolcatlist', array(
				'L_CAT_NAME'	=> $tool_cat,
			));
			
			// Assign the tools inside this cat
			foreach ($cat_tools as $tool)
			{
				$template->assign_block_vars('toolcatlist.toollist', array(
					'L_TOOL_NAME'		=> $tool['TITLE'],
					'L_TOOL_EXPLAIN'	=> $tool['EXPLAIN'],

					'U_TOOL'	=> append_sid(ATK_ROOT_PATH . 'index.' . PHP_EXT, array('t' => $tool['TOOL_NAME'])),
				));
			}
		}

		// Output the page
		$this->phpbb->page_header('tool_box', 'TOOL_BOX');
		$this->phpbb->page_footer();
	}
	
	/**
	* Load the required tool
	* First test whether there is a file with this name in the ATK_TOOL_BOX, then check whether the class is 
	* correctly formatted and finally construct the tool.
	*
	* @param String $tool_name The name of the requested tool
	* @return Object $tool_instance An instance of this tool class
	*/
	function _load_tool($tool_name)
	{
		// Append .PHP_EXT if needed
		if (false === strpos($tool_name, '.'))
		{
			$tool_name = $tool_name . '.' . PHP_EXT;
		}

		// Fetch this tool
		if ((@include(ATK_TOOL_BOX . $tool_name)) === false)
		{
			// @TODO: nice error message
			trigger_error("Tool class couldn't be found! " . $tool_name, E_USER_ERROR);
		}

		// Determain the correct class name
		$class = basename($tool_name, '.' . PHP_EXT);
					
		// Check class name
		if (!class_exists($class))
		{
			// @TODO: nice error message
			trigger_error("Invalid class name in: " . $tool_name, E_USER_ERROR);
		}

		// Construct
		$tool_instance = new $class();
		return $tool_instance;
	}
	
	/**
	* Display the tool options
	*
	* @access private
	*/
	function _tool_options()
	{
		// Construct the tool
		$this->tool = $this->_load_tool($this->req_tool_data['req_tool']);

		// Fetch the options
		$options = $this->tool->get_options();

		// Display the options
		$this->_display_options($options);

		$this->phpbb->page_header('tool_options', 'TOOLNAME');
		$this->phpbb->page_footer();
	}
	
	/**
	* Start the requested tool
	*
	* @access private
	*/
	function _run_tool()
	{
		// Construct the tool
		$this->tool = $this->_load_tool($this->req_tool_data['req_tool']);

		// Call the run method of this tool
		if (method_exists($this->tool, 'run'))
		{
			$this->tool->run();
		}
		else
		{
			// @TODO: localize the error
			trigger_error('Tool {' . $this->req_tool_data['req_tool'] . '} hasn\'t have a runner!');
		}
	}


	//-- Getters
	
	/**
	* Read a config value
	*
	* @access public
	* @param string $name The name of the requested config value
	* @return mixed		  Return the value of the requested config value. Return NULL if not set or empty
	*/
	function get_config($name)
	{
		return (!isset($this->config[$name]) || empty($this->config[$name])) ? NULL : $this->config[$name];
	}

	//-- Un grouped methods
	
	/**
	* Perform some actions that don't allow authentication!
	*
	* @access private
	*/
	function _run_non_authed_tools()
	{
		// These can only be used when there is no tool requested
		if (!isset($_GET['action']) || isset($_GET['t']))
		{
			return;
		}
		
		// Perform actions.
		// To chose use direct comparison against the $_GET['action'] as we can't use request_var here!
		switch($_GET['action'])
		{
			case 'atklogout' :
				global $user;

				// Depending on the way we've logged in
				if ($this->get_config('atk_password'))
				{
					// The cookie name
					global $config;
					$cookie_name	= (isset($_COOKIE[$config['cookie_name'] . '_atk_key'])) ? $config['cookie_name'] . '_atk_key' : 'phpbb_atk_key';
					$name_data		= rawurlencode($cookie_name) . '=' . rawurlencode('');
					header('Set-Cookie: ' . $name_data . '; expires=' . time() - 3600);
				}
				else
				{
					// phpBB style
					$user->unset_admin();
					$redirect_url = append_sid(PHPBB_ROOT_PATH . 'index.' . PHP_EXT);
					meta_refresh(3, $redirect_url);
					trigger_error($user->lang['ADM_LOGGED_OUT'] . '<br /><br />' . sprintf($user->lang['RETURN_INDEX'], '<a href="' . $redirect_url . '">', '</a>'));
				}
			break;
		}
	}


	/**
	* You got to love php4
	*
	* @access public
	*/
	function atk_core(&$config)
	{
		$this->__construct($config);
	}
}
?>